package com.maben.vue_support.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.maben.vue_support.service.WebSSHService;
import com.maben.vue_support.pojo.ssh.SSHConnectInfo;
import com.maben.vue_support.pojo.ssh.WebSSHData;
import com.maben.vue_support.server.SshWebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * WebSSH业务逻辑实现
 */
@Service
@Slf4j(topic = "m.WebSSHServiceImpl")
public class WebSSHServiceImpl implements WebSSHService {
    //存放ssh连接信息的map
    private static Map<String, Object> sshMap = new ConcurrentHashMap<>();
    //线程池
    private ExecutorService executorService = Executors.newCachedThreadPool();

    /**
     * 初始化连接
     */
    @Override
    public void initConnection(String id) {
        JSch jSch = new JSch();
        SSHConnectInfo sshConnectInfo = new SSHConnectInfo();
        sshConnectInfo.setJSch(jSch);
        sshConnectInfo.setWebsocketId(id);
        //将这个ssh连接信息放入map中
        sshMap.put(id, sshConnectInfo);
    }

    /**
     * 处理客户端发送的数据
     */
    @Override
    public void recvHandle(String message, String id) {
        final WebSSHData webSSHData = JSON.toJavaObject(JSONObject.parseObject(message), WebSSHData.class);
        if (SshWebSocketServer.WEBSSH_OPERATE_CONNECT.equals(webSSHData.getOperate())) {//判断是否是连接命令
            //找到刚才存储的ssh连接对象
            SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(id);
            //启动线程异步处理
            executorService.execute(() -> {
                try {
                    connectToSSH(sshConnectInfo, webSSHData, id);
                } catch (JSchException | IOException e) {
                    log.error("webSsh连接异常,异常信息:{}", ExceptionUtils.getStackTrace(e));
                    close(id);
                }
            });
        } else if (SshWebSocketServer.WEBSSH_OPERATE_COMMAND.equals(webSSHData.getOperate())) { //执行命令
            String command = webSSHData.getCommand();
            log.debug("command:{}",command);
            SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(id);
            if (sshConnectInfo != null) {
                try {
                    transToSSH(sshConnectInfo.getChannel(), command);
                } catch (IOException e) {
                    log.error("webSsh连接异常,异常信息:{}",ExceptionUtils.getStackTrace(e));
                    close(id);
                }
            }
        } else {
            log.error("不支持的操作");
            close(id);
        }
    }


    /**
     * 断开ssh连接
     */
    @Override
    public void close(String id) {
        SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(id);
        if (sshConnectInfo != null) {
            //断开连接
            if (sshConnectInfo.getChannel() != null) sshConnectInfo.getChannel().disconnect();
            //map中移除
            sshMap.remove(id);
        }
    }

    /**
     * 使用jsch连接终端
     */
    private void connectToSSH(SSHConnectInfo sshConnectInfo, WebSSHData webSSHData, String id) throws JSchException, IOException {
        Session session = null;
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        //获取jsch的会话
        session = sshConnectInfo.getJSch().getSession(webSSHData.getUsername(), webSSHData.getHost(), webSSHData.getPort());
        session.setConfig(config);
        //设置密码
        session.setPassword(webSSHData.getPassword());
        //连接  超时时间30s
        session.connect(30000);
        //开启shell通道
        Channel channel = session.openChannel("shell");
        //通道连接 超时时间3s
        channel.connect(3000);
        //设置channel
        sshConnectInfo.setChannel(channel);
        //转发消息
        transToSSH(channel, "\r");
        //读取终端返回的信息流
        try (final InputStream inputStream = channel.getInputStream();) {
            //循环读取
            byte[] buffer = new byte[1024];
            //如果没有数据来，线程会一直阻塞在这个地方等待数据。
            int i = 0;
            while ((i = inputStream.read(buffer)) != -1) {
                final String str =  new String(Arrays.copyOfRange(buffer, 0, i));
                log.debug("shell:{}",str);
                SshWebSocketServer.sendInfo(str, id);
            }

        } finally {
            //断开连接后关闭会话
            session.disconnect();
            channel.disconnect();
        }
    }

    /**
     * 将消息转发到终端
     */
    private void transToSSH(Channel channel, String command) throws IOException {
        if (channel != null) {
            OutputStream outputStream = channel.getOutputStream();
            outputStream.write(command.getBytes());
            outputStream.flush();
        }
    }
}
